package com.stone.databinding.util;

import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;

import androidx.core.content.ContextCompat;
import androidx.core.widget.ImageViewCompat;
import androidx.databinding.BindingAdapter;

import com.stone.databinding.R;
import com.stone.databinding.data.Popularity;

public class BindingAdapters {
    /**
     *  Sets the value of the progress bar so that 5 likes will fill it up.
     *
     *  Showcases Binding Adapters with multiple attributes. Note that this adapter is called
     *  whenever any of the attribute changes.
     */
    @BindingAdapter(value = {"app:progressScaled", "android:max"}, requireAll = true)
    public static void setProgress(ProgressBar progressBar, int likesNum, int max) {
        progressBar.setProgress(Math.min(likesNum * max / 5, max));
    }

    /**
     * A Binding Adapter that is called whenever the value of the attribute `app:popularityIcon`
     * changes. Receives a popularity level that determines the icon and tint color to use.
     */
    @BindingAdapter("app:popularityIcon")
    public static void popularityIcon(ImageView view, Popularity popularity) {
        int color = getAssociatedColor(popularity, view.getContext());

        ImageViewCompat.setImageTintList(view, ColorStateList.valueOf(color));

        view.setImageDrawable(getDrawablePopularity(popularity, view.getContext()));
    }

    /**
     * A Binding Adapter that is called whenever the value of the attribute `android:progressTint`
     * changes. Depending on the value it determines the color of the progress bar.
     */
    @BindingAdapter("app:progressTint")
    public static void tintPopularity(ProgressBar view, Popularity popularity) {
        int color = getAssociatedColor(popularity, view.getContext());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            view.setProgressTintList(ColorStateList.valueOf(color));
        }
    }

    /**
     * Unused Binding Adapter to replace the Binding Converter that hides a view if the number
     * of likes is zero.
     */
    @BindingAdapter("app:hideIfZero")
    public static void hideIfZero(View view, int number) {
        view.setVisibility(number == 0 ? View.GONE : View.VISIBLE);
    }

    private static int getAssociatedColor(Popularity popularity, Context context) {
        switch (popularity) {
            case NORMAL:
                return context.getTheme().obtainStyledAttributes(
                        context.getResources().getIntArray(android.R.attr.colorForeground)).getColor(0, 0x000000);
            case POPULAR:
                return ContextCompat.getColor(context, R.color.popular);
            case STAR:
                return ContextCompat.getColor(context, R.color.star);
            default:
                return Color.BLACK;
        }
    }

    private static Drawable getDrawablePopularity(Popularity popularity, Context context){
        switch (popularity) {
            case NORMAL:
                return ContextCompat.getDrawable(context, R.drawable.ic_person_black_96dp);
            case POPULAR:
                return ContextCompat.getDrawable(context, R.drawable.ic_whatshot_black_96dp);
            case STAR:
                return ContextCompat.getDrawable(context, R.drawable.ic_whatshot_black_96dp);
            default:
                return null;
        }
    }

    /**
     * When defined in an [EditText], this [BindingAdapter] will clear the text on focus and
     * set the previous value if the user doesn't enter one. When the focus leaves, it calls
     * the listener that was passed as an argument.
     *
     * Note that `android:selectAllOnFocus="true"` does something similar but not exactly the same.
     *
     * @see [clearTextOnFocus] for a version without a listener.
     */
    @BindingAdapter("clearOnFocusAndDispatch")
    public static void clearOnFocusAndDispatch(EditText view, View.OnFocusChangeListener listener) {
        view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                TextView textView = (TextView) v;
                if (hasFocus) {
                    // Delete contents of the EditText if the focus entered.
                    view.setTag(R.id.previous_value, textView.getText());
                    textView.setText("");
                } else {
                    if (TextUtils.isEmpty(textView.getText())) {
                        CharSequence tag = (CharSequence) textView.getTag(R.id.previous_value);
                        textView.setText(tag != null ? tag : "");
                    }
                    // If the focus left, update the listener
                    listener.onFocusChange(textView, hasFocus);
                }
            }
        });
    }

    /**
     * Clears the text on focus.
     *
     * This method is using extension functions. It's equivalent to:
     * ```
     * @JvmStatic fun clearTextOnFocus(view: EditText, enabled: Boolean)...
     * ```
     */
    @BindingAdapter("clearTextOnFocus")
    public static void clearTextOnFocus(EditText view, Boolean enabled) {
        if (enabled) {
            clearOnFocusAndDispatch(view, null);
        } else {
            view.setOnFocusChangeListener(null);
        }
    }

    /**
     * Hides keyboard when the [EditText] is focused.
     *
     * Note that there can only be one [TextView.OnEditorActionListener] on each [EditText] and
     * this [BindingAdapter] sets it.
     */
    @BindingAdapter("hideKeyboardOnInputDone")
    public static void hideKeyboardOnInputDone(EditText view, Boolean enabled) {
        if (!enabled)
            return;
        TextView.OnEditorActionListener listener = new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_DONE) {
                    view.clearFocus();
                    InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
                }
                return false;
            }
        };
        view.setOnEditorActionListener(listener);
    }

    /*
     * Instead of having if-else statements in the XML layout, you can create your own binding
     * adapters, making the layout easier to read.
     *
     * Instead of
     *
     * `android:visibility="@{viewmodel.isStopped ? View.INVISIBLE : View.VISIBLE}"`
     *
     * you use:
     *
     * `android:invisibleUnless="@{viewmodel.isStopped}"`
     *
     */

    /**
     * Makes the View [View.INVISIBLE] unless the condition is met.
     */
    @BindingAdapter("invisibleUnless")
    public static void invisibleUnless(View view, Boolean visible) {
        view.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
    }

    /**
     * Makes the View [View.GONE] unless the condition is met.
     */
    @BindingAdapter("goneUnless")
    public static void goneUnless(View view, Boolean visible) {
        view.setVisibility(visible ? View.VISIBLE : View.GONE);
    }

    /**
     * In [ProgressBar], [ProgressBar.setMax] must be called before [ProgressBar.setProgress].
     * By grouping both attributes in a BindingAdapter we can make sure the order is met.
     *
     * Also, this showcases how to deal with multiple API levels.
     */
    @BindingAdapter(value={"android:max", "android:progress"}, requireAll = true)
    public static void updateProgress(ProgressBar progressBar, int max, int progress) {
        progressBar.setMax(max);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            progressBar.setProgress(progress, false);
        } else {
            progressBar.setProgress(progress);
        }
    }

    @BindingAdapter("loseFocusWhen")
    public static void loseFocusWhen(EditText view, Boolean condition) {
        if (condition) view.clearFocus();
    }
}
